FE개발자가 개발서버 인프라 구성한 이야기

나는 첫 직장에서 풀스택 개발자로 일했었다. backend랑 infra에도 관심이 많았고 AWS 자격증도 취득했었다. 그래서 그런지 FE개발자로 이직하고 나서도 이것 저것하는걸 되게 좋아했고, 회사의 강요가 없었음에도 내가 하고싶어서 백엔드도 같이 하면 안되냐고 졸랐다. 이전 직장에서 backend는 Node로 했는데 이번 직장은 Spring, Django로 구성되어 있었고 사실 실무경험은 전혀 없다고 봐도 무방했다. 예전에 Spring을 공부했던적이 있었지만 Spring 지식은 깊지 않았고 API 찍어낼(?) 정도는 되었다. 감사하게도 우리 리드분들은 날 믿어줬고 작년 말부터 수 개월 동안 backend도 같이 개발했다.

회사의 일하는 체제가 바뀌면서 여러 프로젝트를 동시에 QA 해야하는 상황이 빈번해졌고 이에 맞는 환경을 구축해야하는 니즈가 생겼다. 그렇게 PO를 맡게 되고 배포 버저닝, 멀티 테스트 환경을 구축하는 프로젝트가 시작되었다.

시작 - 프로세스 정립

먼저 프로세스를 정립해야했다. 기존에 git flow 브랜치 전략을 버리고 새로운 버저닝 시스템을 도입했다. Trunk base에 가까운 버전 관리 전략을 선택했고 프로세스는 아래와 같다.

![[Pasted image 20240510004646.png]]

  1. 프로젝트 kickoff와 동시에 main 브랜치 base의 버전 생성
    • 버전명 kickoff-{kickoff date}-{project summary}
    • 예: 관리자 대시보드 프로젝트 kickoff-240510-dashboard
  2. kickoff 브랜치에서 jira ticket별 feature 브랜치 생성
  3. 개발 진행
  4. kickoff 브랜치로 머지
  5. 배포 일자 및 platform 버전 할당.
    • kickoff 개발 브랜치는 유지하면서 대외적인 버전과 맵핑해서 관리.
    • 예: platform/v1.3.10 <-> kickoff-240510-dashboard 와 동일하게 관리
  6. kickoff 브랜치로 QA 진행
  7. 해당 브랜치로 배포

이런 프로세스로 버전 개발/배포가 이뤄지게 된다. 다만 걱정되는 부분은, kickoff 브랜치 하나하나가 릴리즈 브랜치 사이즈만큼 커지기 때문에 각 브랜치를 병합할 때 conflict가 크게 발생할 수 있고, 휴먼 에러로 이어지기 쉽기 때문에 개발자가 신경써서 관리해야한다.

또한 버전 관리의 문제는 아니지만, 여러 프로젝트를 동시에 진행해야하는 만큼 개개인의 리소스 관리를 잘 하는게 중요하다. 다른 곳은 한사람이 여러 프로젝트를 어떻게 관리하는지, 빡빡한 일정을 어떻게 관리하는지 궁금하다.

인프라 구조

목표는 개발서버를 4개로 분리해서 운영하는 것이다. 기존 환경은 ECS를 통해 각 어플리케이션이 동작하게 구성되어 있었다. 개발 서버를 위해 4배로 dev 환경을 운영하는건 비용도 그렇고 관리도 힘들 것 같았다. 따라서 하나의 인스턴스에 몰아서 관리하려 하였고 도커 환경으로 4개의 개발 환경을 구성하기로 결정했다.

각 개발 서버를 구성하는 최소 조건은 총 3개의 어플리케이션 구성으로 이루어진다.

  1. Next.js 환경의 Frontend
  2. Spring 환경의 Backend
  3. Django 환경의 Backend

테스트환경 구성 이후에는 행동과 조건을 만족해야했다.

  1. 각 어플리케이션은 여러 버전들이 동시에 독립적으로 테스트 가능해야 한다. (개발 테스트)
  2. 각 어플리케이션은 여러 버전들이 동시에 통합 기능 테스트가 가능해야 한다. (QA)
  3. 이전보다 운영 비용으로 개발 환경이 운영되어야 한다.

3번 항목은 내가 회사에 내걸었던 KR중에 하나인데, 원래 ECS 3개를 동시에 운영했던걸 인스턴스를 하나로 줄여서 가능하게 했다.

구성이 완료된 인프라 구조는 아래와 같다.

![[Pasted image 20240512232703.png]]

  1. 어플리케이션은 docker compose로 관리하고 docker image는 ECR에서 관리한다.
  2. Jenkins에서 빌드한 각 어플리케이션을 ECR에 업로드하고 docker compose에서 사용한다.
  3. docker compose로 올라간 어플리케이션들은 각각 고유한 외부 포트를 가진다.
  4. routes53에서 총 12개의 domain을 생성하고 ALB에 연결한다.
  5. ALB에서는 도메인별로 어플리케이션 외부 포트를 지정한다.

이렇게 도커를 이용해서 4개의 개발환경과 총 12개의 어플리케이션을 관리하는 환경을 구성했다.

개선 사항

개발 서버가 많아지다보니 몇번 개발 서버에 어떤 내용이 배포되었는지 PM, QA와 소통이 필요했는데, 이를 위해서 배포 내용을 요약해서 볼 수 있으면 좋겠다고 느꼈다. 그래서 어떤 dev 서버에 어떤 내용이 배포되었는지 알아볼 수 있도록 대시보드를 만들었다. cloud watch로 배포 로그를 저장하고 lambda로 로그를 불러오면서 api gateway가 데이터를 서빙할 수 있도록 endpoint를 구성했다.

![[image.png]] 흐린 이미지로 대체. 과정에서 aws-sdk cloudwatch의 GetMetricData Node API를 사용했는데, 로그 row가 조금만 많아져도 실시간으로 데이터를 fetch 해 오기에는 속도나 안정성에 문제가 있어보였다. API 호출이 5번에 1번꼴로만 성공하고 속도도 굉장히 느렸다. 그래서 배포마다 S3에 파일로 로그 파일을 최신화해서 업로드하고 해당 파일을 fetch해서 가져오고 파싱하는 식으로 구성했다. 물론 S3 로그 파일에는 최근 100건만 저장하도록 했다.

트러블 슈팅

  1. 원래 생각했던 구조는 Nginx를 ALB 자리에 두고싶었으나, SSL 인증서를 AWS Certification Manager로 관리하고 있었기 때문에 해당 구조는 탈락.
  2. 위 구조에서 조직 규모가 커지고 추가로 개발 서버를 구성하거나, 개발서버 내부에 추가 어플리케이션을 구성해해야 할 때, 관리해야하는 route53 도메인이 정비례해서 늘어난다. 관리포인트가 늘어나지만 이정도는 괜찮지 않을까..? 싶긴한데 개선해야한다면 도메인이 아니라 URL path로 관리가 필요할 것 같다.
  3. ECS 구조에서는 executeRole을 지정해서 자체 IAM이 있었기 때문에 AWS 내부 어플리케이션에 추가 권한 없이 접근이 가능하지만, EC2는 수동으로 IAM을 지정해줘야한다. 인스턴스에 IAM 할당으로 해결.
  4. docker compose 환경에서 FE -> BE 통신이 안됨
    1. 처음에는 localhost 포트로 접근하려 했으나 실패
    2. docker compose의 네트워크를 이용해야 한다는걸 깨닫고 docker compose 내부 네트워크로 호스트 명 변경해서 해결.
      1. 수정 후에 예상치 못한 이점인데, host명으로 Sentry 알림에 몇번 dev 서버에서 에러가 발생했는지도 추적이 가능해졌다.